package webmarket.jpacontrollers;

import java.sql.Timestamp;
import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.EntityType;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;

import webmarket.common.ShopcategoryController;
import webmarket.model.Merchandise;
import webmarket.model.Shopcategory;
import webmarket.model.User;

public class ShopJpaController extends JpaControllerBase<Merchandise> {
	private static final long serialVersionUID = 1196294503034526190L;

	public ShopJpaController() {
		super(Merchandise.class);
	}

	public Merchandise createMerchandise(String name, String description, int price, int categoryId) {
		Merchandise merch = new Merchandise();
		UserJpaController ujc = new UserJpaController();
		merch.setUser(ujc.getCurrent());
		merch.setCategoryId(categoryId);
		merch.setName(name);
		merch.setDescription(description);
		merch.setPrice(price);
		super.create(merch);
		return merch;
	}

	public List<Merchandise> getMerchandises(Integer categoryId,
			String username,
			String nameLike,
			Integer priceFrom,
			Integer priceTo,
			String sortCol,
			String sortDir,
			int limit,
			int offset) {
		logger.entry("categoryId:" + categoryId, "username:" + username, "nameLike: " + nameLike, "priceFrom:" + priceFrom, "priceTo:" + priceTo,
				"sortCol:" + sortCol);
		List<Merchandise> result;
		EntityManager em = getEntityManager();
		try {
			CriteriaBuilder cb = em.getCriteriaBuilder();
			CriteriaQuery<Merchandise> cq = em.getCriteriaBuilder().createQuery(Merchandise.class);
			EntityType<Merchandise> merchandise = em.getMetamodel().entity(Merchandise.class);
			Root<Merchandise> root = cq.from(merchandise);
			Predicate predicate = getPredicate(categoryId, username, nameLike, priceFrom, priceTo, cb, merchandise, root);
			cq.where(predicate);
			if ("username".equals(sortCol)) {
				sortCol = "user";
			}
			Order order = super.getOrder(sortCol, sortDir.equals("desc"), em, root);
			cq.orderBy(order);

			TypedQuery<Merchandise> q = em.createQuery(cq);
			q.setFirstResult(offset);
			q.setMaxResults(limit);
			result = q.getResultList();
		} catch (Throwable e) {
			logger.catching(Level.TRACE, e);
			throw e;
		} finally {
			em.close();
		}
		logger.exit(result);
		return result;
	}

	protected Predicate getPredicate(Integer categoryId,
			String username,
			String nameLike,
			Integer priceFrom,
			Integer priceTo,
			CriteriaBuilder cb,
			EntityType<Merchandise> merchandise,
			Root<Merchandise> root) {
		Predicate predicate = cb.conjunction();
		if (categoryId != null) {
			List<Shopcategory> children = ShopcategoryController.getAllChildren(categoryId);
			children.add(ShopcategoryController.get(categoryId));
			Expression<Integer> shopcategoryIdExp = root.get(merchandise.getDeclaredSingularAttribute("categoryId", Integer.class));
			Predicate[] predicates = new Predicate[children.size()];
			Iterator<Shopcategory> catIterator = children.iterator();
			for (int i = 0; catIterator.hasNext(); i++) {
				Shopcategory cat = catIterator.next();
				predicates[i] = cb.equal(shopcategoryIdExp, cat.getId());
			}
			predicate = cb.and(predicate, cb.or(predicates));

		}
		if (!StringUtils.isBlank(username)) {
			Expression<User> userExp = root.get(merchandise.getDeclaredSingularAttribute("user", User.class));
			User user = new UserJpaController().getByUsername(username);
			predicate = cb.and(predicate, cb.equal(userExp, user));
		}
		Expression<String> nameExp = root.get(merchandise.getDeclaredSingularAttribute("name", String.class));
		Predicate nameLikePredicate = PredicateBuilder.createLikePredicate(cb, nameExp, nameLike);
		predicate = cb.and(predicate, nameLikePredicate);
		Expression<Integer> priceExp = root.get(merchandise.getDeclaredSingularAttribute("price", Integer.class));
		Predicate pricePredicate = PredicateBuilder.createRangePredicate(cb, priceExp, priceFrom, priceTo);
		predicate = cb.and(pricePredicate, predicate);
		return predicate;
	}

	public int getCount(Integer categoryId, String username, String nameLike, Integer priceFrom, Integer priceTo) {
		int count;
		EntityManager em = getEntityManager();
		try {
			CriteriaBuilder cb = em.getCriteriaBuilder();
			CriteriaQuery<Long> cq = em.getCriteriaBuilder().createQuery(Long.class);
			EntityType<Merchandise> merchandise = em.getMetamodel().entity(Merchandise.class);
			Root<Merchandise> root = cq.from(merchandise);
			Predicate predicate = getPredicate(categoryId, username, nameLike, priceFrom, priceTo, cb, merchandise, root);
			count = super.getCount(predicate, em, cq, root);
		} catch (Throwable e) {
			logger.catching(Level.TRACE, e);
			throw e;
		} finally {
			em.close();
		}
		return count;
	}

	public void refreshModDate(int merchandiseId) {
		EntityManager em = getEntityManager();
		try {
			Merchandise m = em.find(Merchandise.class, merchandiseId);
			m.setLastchange(new Timestamp(System.currentTimeMillis()));
			em.merge(m);
		} catch (Throwable e) {
			logger.catching(Level.TRACE, e);
			throw e;
		} finally {
			em.close();
		}

	}

	public void buy(int merchId) {
		EntityManager em = getEntityManager();
		em.getTransaction().begin();
		try {
			Merchandise m = em.find(Merchandise.class, merchId);
			User current = new UserJpaController().getCurrent();
			User seller = m.getUser();
			if (current.getMoney() >= m.getPrice()) {
				seller.setMoney(seller.getMoney() + m.getPrice());
				current.setMoney(current.getMoney() - m.getPrice());
				em.merge(seller);
				em.merge(current);
				em.remove(m);
			}
			em.getTransaction().commit();
		} catch (Throwable e) {
			logger.catching(Level.TRACE, e);
			em.getTransaction().rollback();
			throw e;
		} finally {
			em.close();
		}
	}
}
